package org.jingle.sketch
{
    
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    
    import org.jingle.sketch.component.Composite;
    
    /**
     * A DraggableSprite is a Sprite that has the ability to interact
     * with the Mouse in drag and drop scenarios.  All Sprites have
     * the startDrop() and stopDrag() methods, but those methods only
     * update the display list during enterFrame events, instead of
     * during mouseMove events, which leads to choppy dragging.  The
     * DraggableSprite provides a drag() method, similar to startDrag()
     * and a drop() method, similar to stopDrag(), that enable smooth
     * drag and drop operations.
     */
    public class DraggableManager {
    
        // Store the location of the cursor within the sprite
        // so we can position correctly when the cursor moves
        private var x_offset:Number = 0;
        private var y_offset:Number = 0;
        
        // Keep track of the area where dragging is allowed
        // so the sprite can be kept in bounds.
        private var bounds:Rectangle;
        private var sprite:Sprite;
        private var composite:Composite;
        
        private var last:Point;
        
        
        /**
         * Constructor - nothing to do
         */
        public function DraggableManager(composite:Composite) {
            this.composite = composite;
            this.sprite = composite.sprite;
        }
        
        /**
         * Starts a smooth dragging operation, forcing the player to redraw
         * the Sprite after every mouse move.  Cancel the drag() operation
         * by calling the drop() method.
         */
        public function drag(evt:MouseEvent = null, lockCenter:Boolean = false, rectangle:Rectangle = null ):void {
            // Save the cursor position in the sprite so we can adjust
            // the x and y locations correctly when the cursor position
            // chnages based on the lockCenter parameter.
            var pt:Point;
            if ( !lockCenter ) {
                // lockCenter is false, use the mouse coordinates at the point
                pt = sprite.localToGlobal( new Point( sprite.mouseX, sprite.mouseY ) );
            } else {
                // lockCenter is true, ignore the mouse coordinates
                // and use (0,0) instead as the point
                pt = sprite.localToGlobal( new Point( 0, 0 ) );
            }
            
            last = pt;
            
            // Save the offset values so we can compute x and y correctly
            x_offset = pt.x - sprite.x;
            y_offset = pt.y - sprite.y;
            
            // Save the bounds rectangle
            bounds = rectangle;
            
            // Wire the Sprite to the mouse - whenever the mouse moves
            // invoke handleDrag to update the Sprite position
            sprite.stage.addEventListener( MouseEvent.MOUSE_MOVE, handleDrag );
            
            // Detect a drop by listening for mouse up on the stage
            sprite.stage.addEventListener( MouseEvent.MOUSE_UP, drop );
        }
        
        /**
         * Called everytime the mouse moves after the drag() method has
         * been called.  Updates the position of the Sprite based on
         * the location of the mouse cursor.
         */
        private function handleDrag( event:MouseEvent ):void {
            // Set the x and y location based on the mouse position
            var thet:Number = composite.parent.angle * Math.PI / 180;

			sprite.x = event.stageX - x_offset;
            sprite.y = event.stageY - y_offset;
            
            // Keep sprite in bounds if bounds was specified in drag
            if ( bounds != null ) {
                if ( sprite.x < bounds.left ) {
                    sprite.x = bounds.left;
                } else if ( sprite.x > bounds.right ) {
                    sprite.x = bounds.right;
                }
                
                if ( sprite.y < bounds.top ) {
                    sprite.y = bounds.top; 
                } else if ( sprite.y > bounds.bottom ) {
                    sprite.y = bounds.bottom;      
                }
            }
            
            // Force the player to re-draw the sprite after the event.
            // This makes the movement look smooth, unlike startDrag()
            event.updateAfterEvent();
        }
    
        /**
         * Cancels a drag() operation
         */
        public function drop(event:MouseEvent = null):void {
            // The mouse up indicated the drop, so remove the mouse up listener ...
            sprite.stage.removeEventListener( MouseEvent.MOUSE_UP, drop );
            
            // ... and remove the mouse move listener
            sprite.stage.removeEventListener( MouseEvent.MOUSE_MOVE, handleDrag );
        }
    }
}